package com.softserveinc.parser;

import java.io.IOException;
import java.io.InputStream;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import org.xml.sax.Attributes;
import org.xml.sax.ContentHandler;
import org.xml.sax.SAXException;

import android.sax.Element;
import android.sax.ElementListener;
import android.sax.EndTextElementListener;
import android.sax.RootElement;
import android.sax.StartElementListener;
import android.sax.TextElementListener;
import android.util.Log;
import android.util.Xml;

import com.softserveinc.model.Certification;
import com.softserveinc.model.Movie;
import com.softserveinc.model.MovieDetails;

public class XMLParser implements Parser {

	private static final String TAG = XMLParser.class.getSimpleName();

	public XMLParser() {
	}

	@Override
	public List<Movie> parseMovieList(final InputStream feedbackStream)
			throws SAXException {

		if (feedbackStream == null) {
			Log.e(TAG, "feadback is null");
			return null;
		}

		final List<Movie> movies = new ArrayList<Movie>();

		MovieListHandlerProvider handlerProvider = new MovieListHandlerProvider();
		handlerProvider.init(movies);

		try {
			Xml.parse(feedbackStream, Xml.Encoding.UTF_8,
					handlerProvider.getHandler());
		} catch (IOException e) {
			Log.e(TAG, "error: " + e.getMessage(), e);
		} finally {
			try {
				feedbackStream.close();
			} catch (Exception e) {
				Log.e(TAG, e.getMessage(), e);
			}
		}

		return movies;
	}

	@Override
	public MovieDetails parseMovieDetails(final InputStream feedbackStream)
			throws SAXException {

		if (feedbackStream == null) {
			Log.e(TAG, "feadback is null");
			return null;
		}

		MovieDetailsHandlerProvider handlerProvider = new MovieDetailsHandlerProvider();
		handlerProvider.init();

		try {
			Xml.parse(feedbackStream, Xml.Encoding.UTF_8,
					handlerProvider.getHandler());
		} catch (IOException e) {
			Log.e(TAG, "error: " + e.getMessage(), e);
		} finally {
			try {
				feedbackStream.close();
			} catch (Exception e) {
				Log.e(TAG, e.getMessage(), e);
			}
		}

		return handlerProvider.getMovieDetails();
	}

	private class MovieListHandlerProvider {
		private static final String TAG_ROOT = "OpenSearchDescription";
		private static final String TAG_MOVIE = "movie";
		private static final String TAG_MOVIES = "movies";
		private static final String TAG_TITLE = "name";
		private static final String TAG_ID = "id";
		private static final String TAG_POPULARITY = "rating";
		private static final String TAG_IMAGES = "images";
		private static final String TAG_IMAGE = "image";
		private static final String TAG_RELEASE_DATE = "released";
		private static final String TAG_CERTIFICATION = "certification";

		private static final String ATTRIBUTE_IMAGE_TYPE = "type";
		private static final String ATTRIBUTE_IMAGE_SIZE = "size";

		private static final String MOVIE_DATE_FORMAT = "yyyy-MM-dd";

		private static final String POSTER = "poster";
		private static final String SIZE = "thumb";
		private static final String IMAGE_URL = "url";

		private List<Movie> movies;

		private Movie movie = new Movie();

		private RootElement rootElement = new RootElement(TAG_ROOT);
		private Element moviesElement = rootElement.getChild(TAG_MOVIES);
		private Element movieElement;

		private void init(final List<Movie> movies) {
			this.movies = movies;

			movies.clear();

			initMovieTag();
			initIDTag();
			initTitleTag();
			initPopularityTag();
			initReleaseDateTag();
			initCertificationTag();
			initImageTag();
		}

		private void initMovieTag() {
			movieElement = moviesElement.requireChild(TAG_MOVIE);
			movieElement.setElementListener(new ElementListener() {

				@Override
				public void end() {
					movies.add(movie);
				}

				@Override
				public void start(final Attributes attributes) {
					movie = new Movie();
				}
			});
		}

		private void initIDTag() {
			Element element = movieElement.getChild(TAG_ID);
			element.setEndTextElementListener(new EndTextElementListener() {

				@Override
				public void end(final String body) {
					movie.setId(Integer.valueOf(body));
				}
			});
		}

		private void initTitleTag() {
			Element element = movieElement.getChild(TAG_TITLE);
			element.setEndTextElementListener(new EndTextElementListener() {

				@Override
				public void end(final String body) {
					movie.setTitle(body);
				}
			});
		}

		private void initPopularityTag() {
			Element element = movieElement.getChild(TAG_POPULARITY);
			element.setEndTextElementListener(new EndTextElementListener() {

				@Override
				public void end(final String body) {
					movie.setPopularity(Double.valueOf(body));
				}
			});
		}

		private void initCertificationTag() {
			Element element = movieElement.getChild(TAG_CERTIFICATION);
			element.setEndTextElementListener(new EndTextElementListener() {

				@Override
				public void end(final String body) {
					movie.setCertification(Certification
							.getCertificationForName(body));
				}
			});
		}

		private void initReleaseDateTag() {
			Element element = movieElement.getChild(TAG_RELEASE_DATE);
			element.setEndTextElementListener(new EndTextElementListener() {

				@Override
				public void end(final String body) {
					SimpleDateFormat fmt = new SimpleDateFormat(
							MOVIE_DATE_FORMAT);
					if (body == null || body.equals("")) {
						movie.setReleaseDate(null);
					} else {
						Date date = null;
						try {
							date = fmt.parse(body);
						} catch (ParseException e) {
							Log.e(TAG, e.getMessage(), e);
						}

						movie.setReleaseDate(date);
					}
				}
			});
		}

		private void initImageTag() {
			Element images = movieElement.getChild(TAG_IMAGES);
			Element element = images.getChild(TAG_IMAGE);
			element.setTextElementListener(new TextElementListener() {

				@Override
				public void end(final String body) {
				}

				@Override
				public void start(final Attributes attributes) {
					if (POSTER.equalsIgnoreCase(attributes
							.getValue(ATTRIBUTE_IMAGE_TYPE))
							&& SIZE.equalsIgnoreCase(attributes
									.getValue(ATTRIBUTE_IMAGE_SIZE))) {
						Log.w(TAG,
								"poster url: " + attributes.getValue(IMAGE_URL));
						movie.setPosterHttpUrl(attributes.getValue(IMAGE_URL));
					}
				}
			});
		}

		public ContentHandler getHandler() {
			return rootElement.getContentHandler();
		}
	}

	private class MovieDetailsHandlerProvider {
		private static final String TAG_ROOT = "OpenSearchDescription";
		private static final String TAG_MOVIES = "movies";
		private static final String TAG_MOVIE = "movie";
		private static final String TAG_IMAGE = "image";
		private static final String TAG_IMAGES = "images";
		private static final String TAG_CAST = "cast";
		private static final String TAG_PERSON = "person";
		private static final String TAG_OVERVIEW = "overview";
		private static final String TAG_TRAILER_URL = "trailer";

		private static final String ATTRIBUTE_IMAGE_TYPE = "type";
		private static final String ATTRIBUTE_IMAGE_SIZE = "size";
		private static final String ATTRIBUTE_PERSON_JOB = "job";
		private static final String ATTRIBUTE_PERSON_NAME = "name";

		private static final String Actor = "Actor";
		private static final String BACKDROP = "backdrop";
		private static final String SIZE = "thumb";
		private static final String SIZE_POSTER = "poster";
		private static final String IMAGE_URL = "url";

		private static final int ACTORS_NEEDED_TO_DISPLAY = 4;

		private MovieDetails movieDetails;

		private StringBuilder castBuilder;

		private int castSize;
		private int castSizeNeeded;
		private boolean addMoreActors = true;

		private List<String> thumbs = new ArrayList<String>();
		private List<String> posters = new ArrayList<String>();

		private RootElement rootElement = new RootElement(TAG_ROOT);
		private Element moviesElement = rootElement.getChild(TAG_MOVIES);
		private Element movieElement;

		private void init() {
			initMovieTag();
			initCastTag();
			initOverviewTag();
			initTrailerTag();
			initImagesTag();
		}

		private ContentHandler getHandler() {
			return rootElement.getContentHandler();
		}

		private void initMovieTag() {
			movieElement = moviesElement.getChild(TAG_MOVIE);
			movieElement.setStartElementListener(new StartElementListener() {

				@Override
				public void start(final Attributes attributes) {
					movieDetails = new MovieDetails();
				}
			});
		}

		private void initOverviewTag() {
			Element element = movieElement.getChild(TAG_OVERVIEW);
			element.setEndTextElementListener(new EndTextElementListener() {

				@Override
				public void end(final String body) {
					movieDetails.setOverview(body);
				}
			});
		}

		private void initTrailerTag() {
			Element element = movieElement.getChild(TAG_TRAILER_URL);
			element.setEndTextElementListener(new EndTextElementListener() {

				@Override
				public void end(final String body) {
					movieDetails.setTrailerUrl(body);
				}
			});
		}

		private void initCastTag() {
			Element castElement = movieElement.getChild(TAG_CAST);
			castElement.setElementListener(new ElementListener() {

				@Override
				public void end() {
					movieDetails.setCast(castBuilder.toString());
				}

				@Override
				public void start(final Attributes attributes) {
					castBuilder = new StringBuilder();

					castSize = 0;
					castSizeNeeded = ACTORS_NEEDED_TO_DISPLAY;
				}
			});

			initPersonTag(castElement);
		}

		private void initPersonTag(final Element castElement) {
			Element personElement = castElement.getChild(TAG_PERSON);
			personElement.setTextElementListener(new TextElementListener() {

				@Override
				public void end(final String body) {
				}

				@Override
				public void start(final Attributes attributes) {
					if (Actor.equalsIgnoreCase(attributes
							.getValue(ATTRIBUTE_PERSON_JOB))) {

						if (addMoreActors) {
							if (castBuilder.length() > 0) {
								castBuilder.append(", ");
							}

							castBuilder.append(attributes
									.getValue(ATTRIBUTE_PERSON_NAME));
							castSize++;

							if (castSize == castSizeNeeded) {
								addMoreActors = false;
							}
						}
					}
				}
			});
		}

		private void initImagesTag() {
			Element imagesElement = movieElement.getChild(TAG_IMAGES);
			imagesElement.setElementListener(new ElementListener() {

				@Override
				public void end() {
					Log.d(TAG,
							"+++++++++++++++++++++++++++++++++++++++++++++++");

					movieDetails.setThumbHttpUrls(thumbs);
					movieDetails.setThumbHttpUrlsBig(posters);
				}

				@Override
				public void start(final Attributes attributes) {
					thumbs.clear();
					posters.clear();
				}
			});

			initImageTag(imagesElement);
		}

		private void initImageTag(final Element imagesElement) {
			Element imageElement = imagesElement.getChild(TAG_IMAGE);
			imageElement.setTextElementListener(new TextElementListener() {

				@Override
				public void end(final String body) {
				}

				@Override
				public void start(final Attributes attributes) {
					if (BACKDROP.equalsIgnoreCase(attributes
							.getValue(ATTRIBUTE_IMAGE_TYPE))
							&& SIZE.equalsIgnoreCase(attributes
									.getValue(ATTRIBUTE_IMAGE_SIZE))) {

						thumbs.add(attributes.getValue(IMAGE_URL));
					} else if (BACKDROP.equalsIgnoreCase(attributes
							.getValue(ATTRIBUTE_IMAGE_TYPE))
							&& SIZE_POSTER.equalsIgnoreCase(attributes
									.getValue(ATTRIBUTE_IMAGE_SIZE))) {

						posters.add(attributes.getValue(IMAGE_URL));
					}
				}
			});
		}

		private MovieDetails getMovieDetails() {
			return movieDetails;
		}
	}
}
